package com.ctsi.commons.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.xml.stream.*;
import javax.xml.stream.util.StreamReaderDelegate;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * 面向流的xml处理，主要为了取得好的性能。<br>
 * 获取配置的时候可以有好的性能，但是，必须一次取得，即尽量一次取得所有想要的数据。 不要分多次获取 在不同实现表现不一,尤其是cdata,尽量不要使用了
 * 
 * @author yueming
 * 
 */
public class UtilXmlSt {
	private static final Logger log = LoggerFactory.getLogger(UtilXmlSt.class);
	// public static final String filePath = "D:\\trwork\\test\\src\\dtd\\test.xml";
	//
	// public static void main(String[] args) throws IOException, XMLStreamException {
	// URL url = UtilURL.fromFilename(filePath);
	// InputStream input = url.openStream();
	// try {
	// ElementInfo eInfo = new ElementInfo("mailing-address");
	// eInfo.setReadAll(true);
	//
	// getElementInfo(input, eInfo);
	// ElementValue ss = eInfo.getElementValueList().get(0);
	// System.out.println(ss.value);
	// System.out.println(ss.att);
	// System.out.println(ss.subList);
	// System.out.println(eInfo.getElementValueList());
	// } finally {
	// input.close();
	// }
	//
	// }

	private static Map<String, String> getAtt(XMLStreamReader fr) {
		Map<String, String> att = new HashMap<String, String>();
		for (int i = 0, n = fr.getAttributeCount(); i < n; ++i) {
			att.put(fr.getAttributeName(i).getLocalPart(), fr.getAttributeValue(i));

		}
		return att;
	}

	public static void getElementInfo(String input, ElementInfo eInfo) {
		assert (input != null);
		ByteArrayInputStream bis;

		bis = new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8));

		getElementInfo(bis, eInfo);
	}

	public static void getElementInfo(InputStream input, ElementInfo eInfo) {
		getElementInfo(input, eInfo, false);
	}

	public static ElementValue firstChildElement(ElementInfo eInfo, String childElementName) {
		ElementValue ev = eInfo.getFirstElement();
		return ev.getFirstSubElementValue(childElementName);

	}

	/**
	 * 获取一个元素的信息
	 * 
	 * @param input
	 * @param eInfo
	 */
	public static void getElementInfo(InputStream input, ElementInfo eInfo, boolean validate) {
		try {
			XMLInputFactory factory = XMLInputFactory.newInstance();
			log.debug("XMLInputFactory is :{}", factory.getClass());
			factory.setProperty(XMLInputFactory.IS_VALIDATING, validate);

			factory.setProperty(XMLInputFactory.RESOLVER, LocalResolver.getLocalResolver());
			XMLStreamReader xr = factory.createXMLStreamReader(input);

			XMLStreamReader fr = new StreamReaderDelegate(xr) {
				public int next() throws XMLStreamException {
					while (true) {
						int event = super.next();
						switch (event) {
						case XMLStreamConstants.COMMENT:
						case XMLStreamConstants.PROCESSING_INSTRUCTION:
							continue;
						default:
							return event;
						}
					}
				}
			};

			try {
				int event = fr.getEventType();
				boolean found = false;

				int deep = 0;

				List<ElementValue> eValue = new ArrayList<ElementValue>();

				while (true) {

					switch (event) {

					case XMLStreamConstants.START_ELEMENT:
						String tagName = fr.getName().getLocalPart();

						if (!found) {
							if (tagName.equals(eInfo.getTagName())) {
								found = true;
								ElementValue rootValue = new ElementValue(tagName);
								rootValue.att.putAll(getAtt(fr));
								eInfo.addElementValue(rootValue);
								eValue.add(rootValue);

							}
						}

						if (found) {

							deep++;

							if (deep > 1) {
								ElementValue subeValue = new ElementValue(tagName);

								subeValue.att.putAll(getAtt(fr));
								eValue.add(subeValue);
								eValue.get(deep - 2).subList.add(subeValue);
							}

						}

						break;
					case XMLStreamConstants.CHARACTERS:
						// System.out.println("触发CHARACTERS1"+found+"!"+fr.hasText());

						// ibm jdk 6 getText 不能获取CDATA数据，在这里isWhiteSpace=true
						if (found) {
							if (fr.isWhiteSpace())
								break;
							// System.out.println("触发CHARACTERS2");
							eValue.get(deep - 1).value = fr.getText();

						}
						break;

					case XMLStreamConstants.CDATA:
						// System.out.println("触发CDATA1");
						// <propertiy name="href">
						// <![CDATA[<a href=javascript:shoWinfo('${mainUrl}','${newWin}'); >${dataTitle}</a>]]>
						// </propertiy> 对于这样的weblogic不仅仅是CDATA中间部分,还包括CDATA前面的换行

						if (found) {
							if (fr.isWhiteSpace())
								break;
							// System.out.println("触发ENTITY_REFERENCE2");
							eValue.get(deep - 1).value = fr.getText();

						}
						break;

					case XMLStreamConstants.END_ELEMENT:

						if (found) {

							deep--;
							eValue.remove(deep);
						}

						break;
					default:
						break;
					}

					if (found && deep == 0) {
						if (eInfo.isReadAll()) {

							found = false;
						} else
							break;
					}

					if (!fr.hasNext()) {

						break;

					}

					event = fr.next();
				}
			} finally {
				fr.close();
			}
		} catch (Exception e) {
			log.error("getElementInfo error", e);
			throw new RuntimeException(e);
		}
	}

	/**
	 * 获取元素请求对象
	 * 
	 * @author yueming
	 * 
	 */
	public static class ElementInfo implements Serializable {
		private final String tagName;

		private final List<ElementValue> valueList;

		public ElementInfo(String tagName) {
			this.tagName = tagName;
			valueList = new ArrayList<ElementValue>();

		}

		public ElementInfo(String tagName, boolean readAll) {
			this(tagName);
			this.readAll = readAll;
		}

		public List<ElementValue> getElementValueList() {
			return Collections.unmodifiableList(valueList);
		}

		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (!(obj instanceof ElementInfo) || tagName == null)
				return false;
			return tagName.equals(((ElementInfo) obj).getTagName());
		}

		public int hashCode() {
			return tagName == null ? super.hashCode() : tagName.hashCode();
		}

		/**
		 * 是否获取所有元素信息
		 */
		private boolean readAll = false;

		/**
		 * 获取第一个的属性
		 * 
		 * @return
		 */
		public Map<String, String> getFirstElementAtt() {
			if (valueList.isEmpty())
				return null;
			return valueList.get(0).att;
		}

		/**
		 * 获取第一个的值
		 * 
		 * @return
		 */
		public String getFirstElementValue() {
			if (valueList.isEmpty())
				return null;
			return valueList.get(0).value;
		}

		public ElementValue getFirstElement() {
			if (valueList.isEmpty())
				return null;
			return valueList.get(0);
		}

		protected void addElementValue(ElementValue eValue) {
			valueList.add(eValue);
		}

		public String getTagName() {
			return tagName;
		}

		public boolean isReadAll() {
			return readAll;
		}

		public void setReadAll(boolean readAll) {
			this.readAll = readAll;
		}

	}

	public static class ElementValue implements Serializable {
		public final String tagName;

		public ElementValue(String tagName) {
			this.tagName = tagName;
		}

		public String value = null;

		final Map<String, String> att = new HashMap<String, String>();

		final List<ElementValue> subList = new ArrayList<ElementValue>();

		public String toString() {
			return "tagName=" + tagName + "\n" + "value=" + value + "\n";
		}

		/**
		 * 获取第一个
		 * 
		 * @param tagName
		 * @return
		 */
		public ElementValue getFirstSubElementValue(String tagName) {
			for (ElementValue temE : subList) {
				if (temE.tagName.equals(tagName))
					return temE;
			}
			return null;
		}

		public List<ElementValue> getSubElementValue(String tagName) {
			List<ElementValue> result = new ArrayList<ElementValue>();
			for (ElementValue temE : subList) {
				if (temE.tagName.equals(tagName))
					result.add(temE);
			}
			return result;
		}

		public Map<String, String> getAtt() {
			return Collections.unmodifiableMap(att);
		}

		public List<ElementValue> getSubList() {
			return Collections.unmodifiableList(subList);
		}

	}

	public static class LocalResolver implements XMLResolver {

		private static LocalResolver localResolver = null;

		public static LocalResolver getLocalResolver() {
			if (localResolver == null)
				localResolver = new LocalResolver();
			return localResolver;
		}

		public LocalResolver() {

		}

		/**
		 * 处理dtd或者schema文件位置
		 * 
		 * 
		 * @param publicId
		 *            - Public ID of DTD
		 * @param systemId
		 *            - System ID of DTD
		 */
		public Object resolveEntity(String publicId, String systemId, String baseURI, String namespace) {

			if (systemId == null)
				return null;
			// 处理schema,查询scheman位置
			int lastSlash = systemId.lastIndexOf("/");
			URL resourceUrl = null;
			String filename = null;

			if (lastSlash > -1) {
				String temStr = systemId.substring(0, lastSlash);
				int secondLast = temStr.lastIndexOf("/");

				if (secondLast != -1) {
					filename = systemId.substring(secondLast + 1);
					log.debug("load dtd from :{}", filename);
					resourceUrl = UtilURL.fromResource(filename);

				}
			}

			if (resourceUrl == null) {
				if (lastSlash == -1) {
					filename = systemId;
				} else {
					filename = systemId.substring(lastSlash + 1);
				}
				log.debug("load dtd from :{}", filename);
				resourceUrl = UtilURL.fromResource(filename);
			}

			if (resourceUrl != null) {
				InputStream resStream;
				try {
					resStream = resourceUrl.openStream();
				} catch (IOException e) {
					log.error(e.toString());
					return null;
				}

				return resStream;
			} else {
				log.warn("local dtd not found from :{}", systemId);

				return null;
			}

		}

	}
}
